package org.tema.ejb;

//JPA entities
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.ejb.EJB;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import org.tema.entities.Product;
import org.tema.entities.User;
import org.tema.entities.UserGroup;




@Stateless
public class UserBean implements UserBeanLocal {

	@PersistenceContext
	private EntityManager entityManager;
	
	@EJB
	ProductBeanLocal productBean;
	private static String USERGROUP= "appUser";
	private static String ADMINGROUP="appAdmin";

	
	
	public UserBean (){
		
	}
	
	/////////operations on userGroups
	 public UserGroup createUserGroup(boolean isAdmin, String description){
		 UserGroup userGroup = new UserGroup();
		 userGroup.setDescription(description);
		 if (isAdmin){
			 userGroup.setName(USERGROUP);
		 }
		 else {
			 userGroup.setName(ADMINGROUP); 
		 }
		 entityManager.persist(userGroup);
		 return userGroup;
	 }
	////add the specified user to the groups given by their names
	 
	 public UserGroup createUserGroup(String name, String description){
		 UserGroup uGroup = new UserGroup();
		 uGroup.setName(name);
		 uGroup.setDescription(description);
		 entityManager.persist(uGroup);
		 return uGroup;
	 }
	
	public void  addUserToGroup( User user, UserGroup  userGroup ){
		Set <UserGroup> groupList = user.getUserGroups();
		groupList.add(userGroup);
		user.setUserGroups( groupList );	
	}
	
	//find the groups for the user given by name
	@SuppressWarnings("unchecked")
	public Collection <UserGroup>  findUserGroup( String userName ){
		 User u = findUserByName( userName );
		 Collection <UserGroup>  uGroup = ( Collection <UserGroup>) u.getUserGroups();	
		return uGroup;
	}
	
	//find all users in the specified group
	@SuppressWarnings("unchecked")
	public Collection <User> findUsersInGroup( String groupName ){
		UserGroup uGroup =  findUserGroupByName( groupName );
		Collection <User> userList = (Collection <User> ) uGroup.getUserSet();
		return userList;
	}
	
	public UserGroup findUserGroupByName(String name){
		Query query;
		UserGroup userg;
		try{
			query = entityManager.createQuery(" SELECT g FROM UserGroup g WHERE g.name = :name  ");
			query.setParameter("name", name);
			userg =  (UserGroup) query.getSingleResult();
		}
		catch (NoResultException e) {
			throw new IllegalArgumentException("no group with name  '" + name + "'");
		}
		return userg;	
	}
	
	@SuppressWarnings("unchecked")
	public Collection <String> findAllGroups(){
		Query query = entityManager.createQuery(" SELECT g.name FROM UserGroup g   ");
	 	return (Collection <String>) query.getResultList();
	}
	
	@SuppressWarnings("unchecked")
	public Collection <UserGroup> getAllGroups(){
		Query query = entityManager.createQuery(" SELECT g FROM UserGroup g   ");
		return (Collection <UserGroup>) query.getResultList();
	}
	
	/////////encrypt the User password with MD5 
	public  String encryptPassword(String password) throws NoSuchAlgorithmException{
		MessageDigest messageDigest = MessageDigest.getInstance("MD5");
		byte[] bs;
		messageDigest.reset();
		bs = messageDigest.digest(password.getBytes());
		StringBuilder stringBuilder = new StringBuilder();
		//hex encode the digest
		for (int i = 0; i < bs.length; i++){
	    String hexVal = Integer.toHexString(0xFF & bs[i]);
	    if (hexVal.length() == 1){
	      stringBuilder.append("0");
	    }
	    stringBuilder.append(hexVal);
	  }
	  return stringBuilder.toString();
	}

	////////
	
	@SuppressWarnings("unchecked")
	public User addUser( String name, boolean isAdmin, String password, Product product){
		String encryptedPassword = null;
		User beanUser = new User();
		beanUser.setName(name);
		if (isAdmin==false){
			UserGroup userGroup = findUserGroupByName(USERGROUP);
			Set <UserGroup> groupList = new HashSet <UserGroup>();//set of groups to which the user belongs
			groupList.add(userGroup);
			beanUser.setUserGroups( groupList );	
			//add the user to appUsers group
		}	
		else{
			UserGroup userGroup= findUserGroupByName(ADMINGROUP);	
			//add the user to appAdministrators group
			// add user to appUser group 
			Set <UserGroup> groupSet = new HashSet <UserGroup>();//set of groups to which the user belongs
			groupSet.add(userGroup);
			beanUser.setUserGroups(groupSet);
		}
		try{
			encryptedPassword = encryptPassword(password);
		}
		catch( NoSuchAlgorithmException e ){
			e.printStackTrace();
		}
		beanUser.setAdmin(isAdmin);
		//beanUser.setUserGroup(userGroup);
		beanUser.setPassword(encryptedPassword);
		//add the user to a product
		//productBean.updateProduct(product);
		if( product != null){
			Set <Product> productSet = new HashSet <Product>();
			product = productBean.findProductByName(product.getName());
			productSet.add(product);
			beanUser.setProductSet(productSet);
		}
	
		entityManager.persist(beanUser);
		return beanUser;
		
	}
	
	//to be removed because more specific searches have been introduced

	@SuppressWarnings("unchecked")
	public Collection <User> findAllUsers(){
		Query query = entityManager.createQuery(" SELECT c FROM User c ");
		return query.getResultList();
	}
	
	public User updateUser(User user)
	 {
		
		if (user.getAdmin()==false){
			UserGroup userGroup = findUserGroupByName(USERGROUP);
			Set <UserGroup> groupList = new HashSet <UserGroup>();//set of groups to which the user belongs
			groupList.add(userGroup);
			user.setUserGroups( groupList );	
			//add the user to appUsers group
		}	
		else{
			UserGroup userGroup= findUserGroupByName(ADMINGROUP);	
			//add the user to appAdministrators group
			// add user to appUser group 
			Set <UserGroup> groupList = new HashSet <UserGroup>();//set of groups to which the user belongs
			groupList.add(userGroup);
			user.setUserGroups(groupList);
		}
		try{
			user.setPassword(encryptPassword(user.getPassword()));
		}catch( NoSuchAlgorithmException e ){
			e.printStackTrace();
		}
	    entityManager.merge(user);
	    return user;
	}

	public User undoUserChanges(User user) {
	    entityManager.refresh(entityManager.merge(user));
	    return user;                                          
	}

	public void deleteUser(User user) {
	    entityManager.remove(entityManager.merge(user));
	}


	//search methods
	public User  findUserById( int id ){
		User u;
		Query query;
		try {
			 query = entityManager.createQuery(" SELECT c FROM User c WHERE c.id = :id  ");
			 query.setParameter("id", id);
			 u = (User) query.getSingleResult();	 
		}
		catch (NoResultException e) {
			throw new IllegalArgumentException("no user with id '" + id + "'");
		}
		return u;
	}
	
	public User findUserByName( String name ){
		User u;
		Query query;
		try {
			 query = entityManager.createQuery(" SELECT c FROM User c WHERE c.name = :name  ");
			 query.setParameter("name", name);
			 u = (User) query.getSingleResult();	 
		}
		catch (NoResultException e) {
			throw new IllegalArgumentException("no user with name '" + name + "'");
		}
		return u;
	}
	
	@SuppressWarnings("unchecked")
	public Collection <User> findUsersByName( String name ){
		Collection <User> lUsers= null;
		name = name.concat("%");
		Query query;
		query = entityManager.createQuery(" SELECT c FROM User c WHERE c.name LIKE :name  ");
		query.setParameter("name", name);
		lUsers = (Collection <User>) query.getResultList();	 
		return lUsers;
	}
/*	
	public User findUserByPrivilige( Boolean isAdmin ){
		Query query = entityManager.createQuery(" SELECT c FROM User c WHERE c.isAdmin = :isAmin  ");
		query.setParameter("isAdmin", isAdmin);
		return (User) query.getSingleResult();
	}
*/
	@SuppressWarnings("unchecked")
	public Collection <User>  findUserByProductId( int productId ){
		Product product = productBean.findProductById( productId );
		Collection <User> users = product.getUser();
		return (Collection <User>) users;
	}
	
	@SuppressWarnings("unchecked")
	public Collection <User>  findUserByProductName( String  prodName ){
		Product product = productBean.findProductByName( prodName );
		Collection <User> users = product.getUser();
		return (Collection <User>) users;
	}
}
